其他
数据治理 | 不丢数据的秘籍:了解Python的内存管理机制
目录
一、前言
二、 Python 的内存管理机制
浅复制
深复制
内存清理
三、结束语
本文共3180个字,阅读大约需要8分钟,欢迎指正!
Part1前言
在使用 Python 操作数据时,你是否遇到过这样的情况,当你不能确定一个步骤能否成功时,便使用代码另外复制了一份数据以防操作失误导致数据丢失,但当你修改了其中一个数据后,另一个复制的数据也同样发生改变,导致你的处理工作前功尽弃。其实这是不够了解 Python 导致的 “惨剧”,这篇文章我们就来讨论一下 Python 的内存管理机制,让你不再踩 “无效备份” 的坑。Part2Python 的内存管理机制
变量通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值。变量名没有类型,类型属于对象(变量引用对象,所以类型取决于对象),变量引用什么类型的对象,变量就是什么类型。Python 中的 id() 方法用于获取变量对象的内存地址。语法 A is B 用于判断两个引用所指的对象是否相同,若相同,返回 True,否则返回 False。1浅复制
当浅复制一个变量时,内存不会为复制的变量分配空间,而是会将复制的变量指向被复制变量所指向的内存空间,说简单些,就是为数据起了个别名。当修改复制的变量时,实际上修改的是两者共同指向的内存中的对象,此时被复制的变量的值也随之改变。而删除复制的变量时,只是显式地销毁了变量名,即复制的引用方式,不会直接删除变量名所指向内存中的对象。例如:>>> A = [1,2,3]
>>> id(A)
1980982635784
>>> B = A # 浅拷贝的命令
>>> id(B)
1980982635784
>>> B[0] = 9 # 修改其中一个数据
>>> B
[9, 2, 3]
>>> A # 另一个数据同样发生改变
[9, 2, 3]
>>> del B # 删除其中一个变量,另一个依然存在
>>> A
[9, 2, 3]
2深复制
前面提到浅复制,当然也有深复制(也称深拷贝)。浅复制的变量会与被复制的变量共用一个内存对象,改变其中一个,另一个也随之改变。但是当我们不希望两个变量同步改变时就可以使用深复制,深复制的对象会在内存中重新申请空间,存储与被复制变量相同的对象,即使被复制的对象发生改变或者被删除,也不会影响深复制的的变量。深复制的语法和应用实例如下。>>> import copy # 导入 Python 标准库 copy
>>> var1 = [4,5,6]
>>> var2 = copy.deepcopy(var1) # 使用 copy 库深拷贝数据
>>> id(var1) == id(var2)
False
>>> var1[1] = 10 # 修改其中一个
>>> var1
[4, 10, 6]
>>> var2 # 另一个没有发生改变
[4, 5, 6]
>>> del var1
>>> var2 # 深拷贝的变量不受被拷贝变量的任何影响
[4, 5, 6]
有时候你没有进行任何拷贝操作,不同的变量也可能会指向同一个对象,究竟是怎么回事呢?Python 可以使用语法 A is B 进行引用所指判断,判断两个变量 A 和 B 的所指向的内存空间是否相同。下面我们来探索一下:>>> # 整数型
>>> var3 = 100
>>> var4 = 100
>>> var3 is var4 # 两个变量竟然使用同一个内存对象
True
>>> # 浮点数型
>>> var5 = 2.3
>>> var6 = 2.3
>>> var5 is var6
False
>>> # 短字符串
>>> var7 = 'good'
>>> var8 = 'good'
>>> var7 is var8
True
>>> # 长字符串
>>> var9 = 'very good!'
>>> var10 = 'very good!'
>>> var9 is var10
False
>>> # 列表
>>> var11 = [1,2]
>>> var12 = [1,2]
>>> var11 is var12
False
由运行结果可知,Python缓存了整数和短字符串,因此每个对象在内存中只存有一份,引用所指对象就是相同的,即使分别使用赋值语句,也只是创造新的引用,而不是对象本身。Python没有缓存长字符串(可以简单理解为纯字母、数字或者下划线 “_” 的组合 就是短字符串,其他的是长字符串。)、列表及其他对象。Python可以使用 标准库 sys
的 getrefcount()
方法获取一个变量所指向对象被引用的次数。值得注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount() 所得到的结果,总会比期望的多1。例如:3内存清理
为了节省内存空间,当 Python 中的对象越来越多,占据越来越大的内存,启动垃圾回收(garbage collection),将没用的对象清除。其原理是,当 Python 的某个对象的被引用次数降为 0 时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾。比如某个新建对象,被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被当作垃圾回收。>>> var15 = 3245
>>> sys.getrefcount(var15)
2
>>> del var15
>>> sys.getrefcount(var15) # 系统报错,提示变量未定义
Traceback (most recent call last):
File "<pyshell#130>", line 1, in <module>
sys.getrefcount(A)
NameError: name 'var15' is not defined
使用 del var15 之后,已经没有任何引用指向之前建立的 3245,该对象引用计数变为0,此时用户不可能通过任何方式接触或者动用这个对象,当垃圾回收启动时,Python扫描到这个引用计数为0的对象,就将它所占据的内存清空。但是进行垃圾回收时,Python不能进行其他的任务,因此频繁地进行垃圾回收会大大降低 Python 工作效率。Python只会在特定情况下才会主动进行垃圾回收,当垃圾对象少时就没有必要进行垃圾回收。当然,Python也支持主动进行垃圾回收,语法如下:>>> import gc
>>> gc.collect()
为了你的数据安全,通常不建议主动使用 gc.collect()
来清理内存,因为这种命令是危险的,如果使用不当,可能会导致内存中的数据丢失。Part3结束语
浅拷贝与深拷贝的概念与操作是每一个“数据人”必须掌握的技能,也几乎是每一位同学必踩的坑,小编希望你在踩坑之前就看到这篇文章,掌握 Python 的内存机制,避免数据丢失。我们将在数据治理板块中推出一系列原创推文,帮助读者搭建一个完整的社科研究数据治理软硬件体系。该板块将涉及以下几个模块(点击标题即可跳转至相应合集):
星标⭐我们不迷路!想要文章及时到,文末“在看”少不了!
点击搜索你感兴趣的内容吧
往期推荐
数据Seminar
这里是大数据、分析技术与学术研究的三叉路口
文 | 《社科领域大数据治理实务手册》
欢迎扫描👇二维码添加关注